﻿using Roslyn.Compilers.CSharp;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QInjection.Injector
{
    internal class TreeValidator
    {
        private BranchSet _pset;
        private RulesSet _rset;

        internal TreeValidator()
        {
        }

        internal void Run(BranchSet pset, RulesSet rset, SyntaxNode node)
        {
            _pset = pset;
            _rset = rset;

            DumpStart();
            Validate(node, Branch.Input, 1);
            DumpClose();
        }

#if DEBUG
        private StreamWriter _dumper;
#endif

        [Conditional("DEBUG")]
        private void DumpStart()
        {
            //_dumper = new System.IO.StreamWriter("dump.txt");
        }

        [Conditional("DEBUG")]
        private void Dump(Type ntype, Branch tp, Int32 Depth)
        {
            //for (int i = 0; i < Depth; i++) _dumper.Write(' ');
            //_dumper.WriteLine("{0} :: {1}", tp, ntype);
        }

        [Conditional("DEBUG")]
        private void DumpClose()
        {
            //_dumper.Close();
        }

        private void ValidateType(Type type, Branch branch)
        {
            if (!_pset.ContainsKey(branch))
                throw new Exception(String.Format("{0} Branch undefined", branch));

            if (!_pset[branch].Contains(type))
                throw new Exception(String.Format("Node {0} is not recognized as {1} Branch", type.FullName, branch));
        }

        private void Validate(SyntaxNode node, Branch branch, Int32 Depth)
        {
            if (branch == Branch.Input)
            {
                var ntype = node.GetType();

                ValidateType(ntype, Branch.Input);

                SimpleLambdaExpressionSyntax n = (SimpleLambdaExpressionSyntax)node;

                _rset.Validate(ntype, n);

                Dump(ntype, branch, Depth);

                Validate(n.Body, Branch.Body, Depth + 1);
                Validate(n.Parameter, Branch.Parameter, Depth + 1);
            }
            else
            {
                var ntype = node.GetType();

                ValidateType(ntype, branch);

                _rset.Validate(ntype, node);

                Dump(ntype, branch, Depth);

                if (ntype == typeof(BinaryExpressionSyntax))
                {
                    BinaryExpressionSyntax n = (BinaryExpressionSyntax)node;
                    Validate(n.Left, Branch.LeftMember, Depth + 1);
                    Validate(n.Right, Branch.RightMember, Depth + 1);
                }
                else if (ntype == typeof(LiteralExpressionSyntax))
                {
                    LiteralExpressionSyntax n = (LiteralExpressionSyntax)node;
                }
                else if (ntype == typeof(IdentifierNameSyntax))
                {
                    IdentifierNameSyntax n = (IdentifierNameSyntax)node;
                }
                else if (ntype == typeof(ParameterSyntax))
                {
                    ParameterSyntax n = (ParameterSyntax)node;
                }
                else if (ntype == typeof(MemberAccessExpressionSyntax))
                {
                    MemberAccessExpressionSyntax n = (MemberAccessExpressionSyntax)node;
                    Validate((SyntaxNode)n.Expression, Branch.Expression, Depth + 1);
                }
                else if (ntype == typeof(InvocationExpressionSyntax))
                {
                    InvocationExpressionSyntax n = (InvocationExpressionSyntax)node;
                    foreach (var arg in n.ArgumentList.Arguments)
                        Validate((SyntaxNode)arg, Branch.Argument, Depth + 1);
                }
                else if (ntype == typeof(ArgumentSyntax))
                {
                    ArgumentSyntax n = (ArgumentSyntax)node;
                    Validate((SyntaxNode)n.Expression, Branch.Expression, Depth + 1);
                }
                else if (ntype == typeof(PrefixUnaryExpressionSyntax))
                {
                    PrefixUnaryExpressionSyntax n = (PrefixUnaryExpressionSyntax)node;
                    Validate((SyntaxNode)n.Operand, Branch.Expression, Depth + 1);
                }
                else if (ntype == typeof(PostfixUnaryExpressionSyntax))
                {
                    PostfixUnaryExpressionSyntax n = (PostfixUnaryExpressionSyntax)node;
                    Validate((SyntaxNode)n.Operand, Branch.Expression, Depth + 1);
                }
                else if (ntype == typeof(ConditionalExpressionSyntax))
                {
                    ConditionalExpressionSyntax n = (ConditionalExpressionSyntax)node;
                    Validate(n.Condition, Branch.Condition, Depth + 1);
                    Validate(n.WhenFalse, Branch.WhenFalse, Depth + 1);
                    Validate(n.WhenTrue, Branch.WhenTrue, Depth + 1);
                }
                else if (ntype == typeof(ParenthesizedExpressionSyntax))
                {
                    ParenthesizedExpressionSyntax n = (ParenthesizedExpressionSyntax)node;
                    Validate(n.Expression, Branch.Expression, Depth + 1);
                }
                else
                    throw new Exception(String.Format("Unexpected node type {0}", ntype));
            }
        }

    }
}
